package org.budo.support.lang.util;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;

import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author limingwei2
 */
public class FileUtil {
    private static final Logger log = Slf4j.getLogger();

    public static void copyDir(File from, File to, FileFilter filter) throws Throwable {
        if (!from.isDirectory()) {
            throw new RuntimeException("#15 from is not dir, from=" + from);
        }

        if (!to.exists()) {
            to.mkdirs();
        }

        File[] files = from.listFiles();

        for (File file : files) {
            if (null != filter && !filter.accept(file)) {
                continue;
            }

            if (file.isDirectory()) {
                copyDir(new File(from, file.getName()), new File(to, file.getName()));
                continue;
            }

            if (file.isFile()) {
                copyFile(new File(from, file.getName()), new File(to, file.getName()));
                continue;
            }

            throw new RuntimeException("#79 file is not file or directory, file=" + file);
        }
    }

    /**
     * 复制目录
     */
    public static void copyDir(File from, File to) throws Throwable {
        copyDir(from, to, null);
    }

    /**
     * 复制文件
     */
    public static void copyFile(File from, File to) throws Throwable {
        if (!to.exists()) {
            File parentFile = to.getParentFile();
            parentFile.mkdirs();
        }

        FileInputStream inputStream = new FileInputStream(from);
        FileOutputStream outputStream = new FileOutputStream(to);

        IoUtil.write(inputStream, outputStream);
    }

    public static String getCodeSourceLocation(Class<?> type) {
        String url = type.getProtectionDomain().getCodeSource().getLocation().toString();
        int a = url.lastIndexOf('/');
        String b = url.substring(0, a + 1);
        return b.replace("file:", "");
    }

    public static Boolean exists(String path) {
        if (null == path) {
            return false;
        }

        File file = new File(path);
        return file.exists();
    }

    public static File getCanonicalFile(File file) {
        if (null == file) {
            return null;
        }

        try {
            return file.getCanonicalFile();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static String getCanonicalPath(File file) {
        if (null == file) {
            return null;
        }

        try {
            return file.getCanonicalPath();
        } catch (IOException e) {
            throw new RuntimeException("#114 file=" + file, e);
        }
    }

    public static FileTime getLastModifiedTime(Path path, LinkOption... options) {
        try {
            return Files.getLastModifiedTime(path, options);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static String getSuffix(String originalFilename) {
        if (StringUtil.isEmpty(originalFilename)) {
            log.error("#119 getSuffix error, originalFilename is null, originalFilename=" + originalFilename);
            return originalFilename;
        }

        int i = originalFilename.lastIndexOf('.');
        if (i < 0) {
            log.error("#125 getSuffix error, originalFilename has no dot, originalFilename=" + originalFilename);
            return originalFilename;
        }
        if (i >= originalFilename.length() - 1) {
            log.error("#129 getSuffix error, dot is at the end , originalFilename=" + originalFilename);
            return originalFilename;
        }

        return originalFilename.substring(i + 1);
    }

    public static SeekableByteChannel newByteChannel(Path path, OpenOption... options) {
        try {
            return Files.newByteChannel(path, options);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static void createNewFile(String filePath) {
        if (null == filePath || filePath.isEmpty()) {
            throw new IllegalArgumentException("#155 filePath is null");
        }

        createNewFile(new File(filePath));
    }

    /**
     * 如果存在则忽略
     */
    public static void createNewFile(File file) {
        if (null == file) {
            throw new IllegalArgumentException("#166 file is null");
        }

        if (file.exists()) {
            return;
        }

        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }

        try {
            file.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException("#221 error, canonicalPath=" + getCanonicalPath(file), e);
        }
    }

    public static RandomAccessFile newRandomAccessFile(File file, String mode) {
        try {
            return new RandomAccessFile(file, mode);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public static String probeContentType(Path path) {
        try {
            return Files.probeContentType(path);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static void deleteIfExists(String path) {
        File file = new File(path);
        deleteIfExists(file);
    }

    public static void deleteIfExists(File file) {
        if (file.exists()) {
            file.delete();
        }
    }

    public static Long fileSize(Path path) {
        if (null == path) {
            return null;
        }

        if (!Files.exists(path)) {
            return 0L;
        }

        try {
            return Files.size(path);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static File createTempFile(String prefix, String suffix) {
        try {
            String tmpDir = System.getProperty("java.io.tmpdir");
            if (StringUtil.isEmpty(tmpDir)) {
                log.error("#224 tmpDir=" + tmpDir);
                return null;
            }

            if (!exists(tmpDir)) {
                new File(tmpDir).mkdirs();
                log.warn("#230 create tmpDir, " + tmpDir);
            }

            return File.createTempFile(prefix, suffix);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
